/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is
*   Nick Kreeger
* Portions created by the Initial Developer are Copyright (C) 2006-2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*   Nick Kreeger <nick.kreeger@park.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

#import "MailboxesDataSource.h"
#import "MailboxItem.h"
#import "MessageAccountManager.h"
#import "MessageWindowService.h"
#import "MailWindowController.h"

#import "nsIMsgAccountManager.h"
#import "nsIMsgAccount.h"
#import "nsIMsgIncomingServer.h"
#import "nsIMsgFolder.h"
#import "nsISupportsArray.h"
#import "nsIEnumerator.h"
#import "nsMsgBaseCID.h"

#import "NSString+Utils.h"

@interface MailboxesDataSource(Private)

-(void)createFolderDataSource;

-(void)addChildrenForMailboxItem:(MailboxItem*)aMailboxItem;
-(void)mailboxSubFoldersDidChange:(NSNotification*)aSender;

@end

@implementation MailboxesDataSource

-(id)init
{
  if ((self = [super init]))
  {
    mRootMailbox = [[MailboxItem alloc] init];
    [self createFolderDataSource];
    mMailboxToUpdate = nil;
    mShouldUpdateAfterTransmission = NO;
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(mailboxSubFoldersDidChange:) 
                                                 name:kMailboxDidAddSubfolder 
                                               object:nil];
  }
  
  return self;
}

-(void)dealloc
{
  if (mRootMailbox)
    [mRootMailbox release];
  
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  
  [super dealloc];
}

-(void)createFolderDataSource
{
  nsresult rv;
  nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
  if (NS_FAILED(rv))
    return;
  
  nsCOMPtr<nsISupportsArray> accountsArray;
  accountManager->GetAccounts(getter_AddRefs(accountsArray));
  if (NS_FAILED(rv))
  {
#if DEBUG
    NSLog(@"Could not get the accounts, returning");
#endif
    return;
  }
  
  PRUint32 arrSize;
  rv = accountsArray->Count(&arrSize);
  if (NS_FAILED(rv) || arrSize == 0)
  {
#if DEBUG
    NSLog(@"There are no accounts, returning");
#endif
    return;
  }
  
  PRUint32 i;
  for (i = 0; i < arrSize; i++)
  {
    nsCOMPtr<nsIMsgAccount> curMsgAccount = do_QueryInterface(accountsArray->ElementAt(i), &rv);
    if (NS_FAILED(rv) || !curMsgAccount)
      continue;
    
    nsCOMPtr<nsIMsgIncomingServer> curIncomingServer;
    rv = curMsgAccount->GetIncomingServer(getter_AddRefs(curIncomingServer));
    if (NS_FAILED(rv) || !curIncomingServer)
      continue;

    nsCOMPtr<nsIMsgFolder> curRootMsgFolder;
    rv = curIncomingServer->GetRootMsgFolder(getter_AddRefs(curRootMsgFolder));
    if (NS_FAILED(rv) && !curRootMsgFolder)
      continue;
    
    // Create a MailboxItem instance for the root folder.
    MailboxItem* curRootMailboxItem = [[MailboxItem alloc] initWithMsgFolder:curRootMsgFolder isRootFolder:YES];
    
    nsXPIDLCString curAccountKey;
    rv = curMsgAccount->GetKey(getter_Copies(curAccountKey));
    if (NS_SUCCEEDED(rv))
      [curRootMailboxItem setAccountKey:[NSString stringWithCString:curAccountKey]];
    
    nsCOMPtr<nsIEnumerator> subFoldersEnum;
    rv = curRootMsgFolder->GetSubFolders(getter_AddRefs(subFoldersEnum));
    if (NS_FAILED(rv))
      continue;
    
    // Add any child folders to to accounts root folder.
    nsCOMPtr<nsIMsgFolder> curMsgFolder;
    nsCOMPtr<nsISupports> item;
    for (rv = subFoldersEnum->First(); NS_SUCCEEDED(rv); rv = subFoldersEnum->Next())
    {
      rv = subFoldersEnum->CurrentItem(getter_AddRefs(item));
      if (NS_SUCCEEDED(rv) && item)
      {
        curMsgFolder = do_QueryInterface(item, &rv);
        if (NS_SUCCEEDED(rv))
        {
          MailboxItem* curMailboxItem = [[MailboxItem alloc] initWithMsgFolder:curMsgFolder isRootFolder:NO];
          [curMailboxItem setDataSourceOwner:self];
          [curRootMailboxItem addChild:curMailboxItem];
          [curMailboxItem release];
          //XXX check for sub-sub folders?
        }
      }
    }
    
    [mRootMailbox addChild:curRootMailboxItem];
    [curRootMailboxItem release];
  }
}

-(void)mailboxSubFoldersDidChange:(NSNotification*)aSender
{
  // Check to make sure that the application isn't transmitting
  if ([[[MessageWindowService sharedInstance] currentMailWindowController] isTransmitting])
  {
    mShouldUpdateAfterTransmission = YES;
    mMailboxToUpdate = (MailboxItem*)[aSender object];
  }
  else
    [self rebuildMailboxItem:[aSender object]];
}

-(void)addChildrenForMailboxItem:(MailboxItem*)aMailboxItem
{
  if (!aMailboxItem)
    return;
  
  nsresult rv;
  nsCOMPtr<nsIMsgFolder> rootFolder = [aMailboxItem representedMsgFolder];
  if (!rootFolder)
    return;
  
  nsCOMPtr<nsIEnumerator> subFoldersEnum;
  rv = rootFolder->GetSubFolders(getter_AddRefs(subFoldersEnum));
  if (NS_SUCCEEDED(rv) && subFoldersEnum)
  {
    nsCOMPtr<nsIMsgFolder> curMsgFolder;
    nsCOMPtr<nsISupports> supports;
    for (rv = subFoldersEnum->First(); NS_SUCCEEDED(rv); rv = subFoldersEnum->Next())
    {
      rv = subFoldersEnum->CurrentItem(getter_AddRefs(supports));
      if (NS_SUCCEEDED(rv) && supports)
      {
        curMsgFolder = do_QueryInterface(supports);
        MailboxItem* curMailboxItem = [[MailboxItem alloc] initWithMsgFolder:curMsgFolder 
                                                                 isRootFolder:NO];
        [curMailboxItem setDataSourceOwner:self];
        [aMailboxItem addChild:curMailboxItem];
        [curMailboxItem release];
      }
    }
  }
}

-(void)rebuildFolderList;
{
  [mRootMailbox removeAllChildren];
  [self createFolderDataSource];
  
  nsresult rv;
  nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv))
    accountManager->SaveAccountInfo();
}

-(void)rebuildMailboxItem:(MailboxItem*)aMailboxItem
{
  if (!aMailboxItem)
    return;
  
  [aMailboxItem removeAllChildren];
  [self addChildrenForMailboxItem:aMailboxItem];
}

-(void)setMailboxesOutlineView:(NSOutlineView*)aOutlineView
{
  mMailboxesOutlineView = aOutlineView;
}

-(void)updateMailboxItem:(MailboxItem*)aMailboxItem
{
  [mMailboxesOutlineView reloadItem:aMailboxItem];
}

-(NSArray*)rootAccountFolderList
{
  return [mRootMailbox children];
}

-(void)onStopTransmission
{
  if (mShouldUpdateAfterTransmission)
  { 
    mShouldUpdateAfterTransmission = NO;
    // Store the name of the currently selected folder, this seems to be the 
    // only piece of comparable information that is available to IMAP folders
    // that are created initially.
    NSString* selectedName = [(MailboxItem*)[mMailboxesOutlineView itemAtRow:[mMailboxesOutlineView selectedRow]] displayName];
    
    [self rebuildMailboxItem:mMailboxToUpdate];
    [mMailboxesOutlineView reloadItem:mMailboxToUpdate reloadChildren:YES];    
    
    // Select the previously selected index.
    BOOL hasFoundIndex = NO;
    for (unsigned int i = 0; i < [[mMailboxToUpdate children] count] && !hasFoundIndex; i++)
    {
      MailboxItem* curMailboxItem = [mMailboxToUpdate childAtIndex:i];
      if ([[curMailboxItem displayName] isEqualToString:selectedName])
      {
        [mMailboxesOutlineView selectRow:[mMailboxesOutlineView rowForItem:curMailboxItem] byExtendingSelection:NO];
        hasFoundIndex = YES;
      }
    }
    
    mMailboxToUpdate = nil;
  }
}

#pragma mark -

//
// NSOutlineViewDataSource Protocol Methods
//
-(id)outlineView:(NSOutlineView*)inOutlineView child:(int)inIndex ofItem:(id)inItem 
{
  MailboxItem* curItem = (MailboxItem*)(inItem ? inItem : mRootMailbox);
  return [curItem childAtIndex:inIndex];
}

-(id)outlineView:(NSOutlineView*)inOutlineView objectValueForTableColumn:(NSTableColumn*)inTableColumn 
           byItem:(id)inItem 
{
  // Since we only have one table column (for now). Just apply the name of the object.
  return [inItem displayName];
}

-(int)outlineView:(NSOutlineView*)inOutlineView numberOfChildrenOfItem:(id)inItem 
{
  // For some reason, we don't point this thing at the correct start index.
  // For that reason, we want to check to see if inItem is nil, if it is, get
  // the number of children for our top level item.
  id item = nil;
  if (!inItem)
    item = mRootMailbox;
  else
    item = inItem;
    
  return [item numberOfChildren];
}

-(BOOL)outlineView:(NSOutlineView*)inOutlineView isItemExpandable:(id)inItem 
{
  if (inItem != nil && [inItem isGroup])
    return YES;
  
  return NO;
}

@end
